from math import *
from neuronConnexion import *

class Neuron:
    """This class simulates neurons and is used by NeuralNetwork class"""
    DEFAULT_THRESHOlD=0
    DEFAULT_THRESHOLD_METHOD=tanh
    
    def __init__(self):
        """default constructor"""
        self.parentConnexions=[]
        self.childConnexions=[]
        self.threshold=0
        self.thresholdMethod=tanh
        self.isComputed=False
            
    def computeOutput(self):
        """This is the default computing method for intermediary neurons"""
        self.input=0
        for connexion in self.parentConnexions:
            if not connexion.isReady():
                return False

        for parentConnexion in self.parentConnexions:
            self.input+=parentConnexion.getSignal()
        self.output=self.thresholdMethod(self.input)
        self.isComputed=True
        self.propagateSignal(self.output)

    def computeSensorOutput(self,inpt):
        """This is the method for sensor neurons"""
        self.output=inpt
        self.isComputed=True
        self.propagateSignal(self.output)

    def propagateSignal(self,input):
        self.computed=False
        if len(self.childConnexions)!=0:
            for child in self.childConnexions:
                child.propagate(input)
        else:
            #This is an motor neuron
            self.result=input

    def getOutput(self):
        """Deprecated, we should never get Output"""
        if self.hasComputed():
            self.setComputed(False)
            return self.output
        print("Neuron :%s is not ready aborting"%(self.id))
        return "Not ready"

    def getResult(self):
        """Returns the output of motor neurons, crashes an error if called for non motor neurons"""
        return self.result

    def getThresholdMethod(self):
        return self.thresholdMethod

    def setThresholdMethod(self,method):
        self.thresholdMethod=method

    def hasComputed(self):
        if self.isComputed:
            return True
        return False

    def setId(self,id):
        self.id=id

    def getId(self):
        return self.id

    def setComputed(self,val):
        self.isComputed=val

    def addParent(self,parentConnexion):
        self.parentConnexions.append(parentConnexion)

    def hasParents(self):
        if len(self.parentConnexions)==0:
            return False
        return True

    def addChild(self,childConnexion):
        self.childConnexions.append(childConnexion)

    def hasChildren(self):
        if len(self.childConnexions)==0:
            return False
        return True
        
    def __str__(self):
        return "%i parents, %i children" %(len(self.parentConnexions),len(self.childConnexions))

